////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.  All rights reserved.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// Portions Copyright (c) 2012 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "..\..\..\..\DeviceCode\Targets\Native\FM4\DeviceCode\FM4.h"
#include "KS_MB9BF568_TouchPanel_Driver.h"

//--//
//#define DEBUG_TOUCHPANEL
struct KS_MB9BF568_TouchPanel_Driver g_KS_MB9BF568_TouchPanel_Driver;

BOOL KS_MB9BF568_TouchPanel_Driver::CalibrationPointGet(TOUCH_PANEL_CALIBRATION_POINT *pTCP)
{
    INT32   cDisplayWidth  = pTCP->cDisplayWidth;
    INT32   cDisplayHeight = pTCP->cDisplayHeight;
    int CalibrationRadiusX = cDisplayWidth  / 20;
    int CalibrationRadiusY = cDisplayHeight / 20;
    switch (pTCP -> PointNumber)
    {
    case    0:
        pTCP->CalibrationX = cDisplayWidth  / 2;
        pTCP->CalibrationY = cDisplayHeight / 2;
        break;

    case    1:
        pTCP->CalibrationX = CalibrationRadiusX * 2;
        pTCP->CalibrationY = CalibrationRadiusY * 2;
        break;

    case    2:
        pTCP->CalibrationX = CalibrationRadiusX * 2;
        pTCP->CalibrationY = cDisplayHeight - CalibrationRadiusY * 2;
        break;

    case    3:
        pTCP->CalibrationX = cDisplayWidth  - CalibrationRadiusX * 2;
        pTCP->CalibrationY = cDisplayHeight - CalibrationRadiusY * 2;
        break;

    case    4:
        pTCP->CalibrationX = cDisplayWidth - CalibrationRadiusX * 2;
        pTCP->CalibrationY = CalibrationRadiusY * 2;
        break;

    default:
        pTCP->CalibrationX = cDisplayWidth  / 2;
        pTCP->CalibrationY = cDisplayHeight / 2;

        return FALSE;
    }
    return TRUE;
}

HRESULT KS_MB9BF568_TouchPanel_Driver::GetDeviceCaps(unsigned int iIndex, void* lpOutput)
{
    if ( lpOutput == NULL )
    {
        return FALSE;
    }
    switch ( iIndex )
    {
    case TOUCH_PANEL_SAMPLE_RATE_ID:
    {
        TOUCH_PANEL_SAMPLE_RATE   *pTSR = (TOUCH_PANEL_SAMPLE_RATE*)lpOutput;

        pTSR->SamplesPerSecondLow       = g_TouchPanel_Sampling_Settings.SampleRate.SamplesPerSecondLow;
        pTSR->SamplesPerSecondHigh      = g_TouchPanel_Sampling_Settings.SampleRate.SamplesPerSecondHigh;
        pTSR->CurrentSampleRateSetting  = g_TouchPanel_Sampling_Settings.SampleRate.CurrentSampleRateSetting;
        pTSR->MaxTimeForMoveEvent_ticks = g_TouchPanel_Sampling_Settings.SampleRate.MaxTimeForMoveEvent_ticks;
    }
    break;

    case TOUCH_PANEL_CALIBRATION_POINT_COUNT_ID:
    {
        TOUCH_PANEL_CALIBRATION_POINT_COUNT *pTCPC = (TOUCH_PANEL_CALIBRATION_POINT_COUNT*)lpOutput;

        pTCPC->flags              = 0;
        pTCPC->cCalibrationPoints = 5;
    }
    break;

    case TOUCH_PANEL_CALIBRATION_POINT_ID:
        return(CalibrationPointGet((TOUCH_PANEL_CALIBRATION_POINT*)lpOutput));

    default:
        return FALSE;
    }
    return TRUE;
}

void KS_MB9BF568_TouchPanel_Driver::GetPoint(TOUCH_PANEL_SAMPLE_FLAGS* pTipState, int* pSource, int* pUnCalX, int* pUnCalY)
{
    static BOOL stylusDown = FALSE;
    *pTipState = 0;
    *pUnCalX = 0;
    *pUnCalY = 0;
    *pSource = 0;

    if (stylusDown)
        *pTipState |= TouchSamplePreviousDownFlag;
    if ((g_KS_MB9BF568_TouchPanel_Driver.m_global_touch == 1)) {
        *pTipState |= TouchSampleValidFlag;
        *pUnCalX = g_KS_MB9BF568_TouchPanel_Driver.m_global_inY;  // intentionally changed x <-> y
        *pUnCalY = g_KS_MB9BF568_TouchPanel_Driver.m_global_inX;  // intentionally changed x <-> y
        *pTipState |= TouchSampleDownFlag;
        stylusDown = true;
    } else {
        *pUnCalX = -1;
        *pUnCalY = -1;
        stylusDown = false;
    }
}

void KS_MB9BF568_TouchPanel_Driver::TouchCompletion(void* arg)
{
    g_KS_MB9BF568_TouchPanel_Driver.Poll();
    if (!g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.IsLinked()) {
        g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.EnqueueDelta(g_KS_MB9BF568_TouchPanel_Driver.m_samplingTimespan);
    } else
    {
        g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.Abort();
    }
}

FM4_ADC_TypeDef *ADC_REGS[] = {
    FM4_ADC0,
    FM4_ADC1,
    FM4_ADC2,
};

void Touch_AD_Initialize(UINT32 ch, UINT32 no)
{
    if (ch >= sizeof(ADC_REGS)/sizeof(FM4_ADC_TypeDef *))
        return;
    FM4_ADC_TypeDef *adc = ADC_REGS[ch];
    adc->ADCEN = ADCEN_ENBL;
    while((adc->ADCEN & ADCEN_READY) != ADCEN_READY) ;
    if (no < 8) {
        adc->SCIS0 = (1 << no);
        adc->ADSS0 = (0 << 0);
    } else if (no < 16) {
        adc->SCIS1 = (1 << (no - 8));
        adc->ADSS1 = (0 << 0);
    } else if (no < 24) {
        adc->SCIS2 = (1 << (no - 16));
        adc->ADSS2 = (0 << 0);
    } else if (no < 32) {
        adc->SCIS3 = (1 << (no - 24));
        adc->ADSS3 = (0 << 0);
    }
    adc->ADST0 = 0xA9;      // PCLK 72MHz=14ns -> 14ns x 64 x 10 = 8960ns
    adc->CMPCR = no;
    adc->ADSR = ADSR_FDAS;  // ADSTP(R/W):0, FDAS(R/W):1(LSB), , , , PCNS(R):0, PCS(R):0, SCS(R):0
    adc->ADCR = 0;          // SCIF:0, PCIF:0, CMPIF:0, , SCIE:0 , PCIE:0 , CMPIE:0, OVRIE:0
    adc->SCCR |= SCCR_SFCLR;
    adc->SCCR &= ~SCCR_RPT;
}

void Touch_AD_Start(UINT32 ch, UINT32 no)
{
    FM4_ADC_TypeDef *adc = ADC_REGS[ch];
    adc->SCCR |= SCCR_SSTR;    // A/D start
}

UINT16 Touch_AD_Sense(UINT32 ch, UINT32 no)
{
    UINT32 value;
    FM4_ADC_TypeDef *adc = ADC_REGS[ch];
    do {
        value = adc->SCFD;
    } while (((value & SCFDL_INVL) != 0) || (no != (value & 0x1F)));
    return (UINT16)(value >> 16);
}

BOOL KS_MB9BF568_TouchPanel_Driver::Initialize(GPIO_INTERRUPT_SERVICE_ROUTINE touchIsrProc)
{
    // ADC channel 0 for other NETMF
    //FM4_GPIO->ADE &= 0x007F0000;
    //FM4_GPIO->PFRB &= ~0x0000007F;
    Touch_AD_Initialize(AD_X_CH, AD_X_NO);
    Touch_AD_Initialize(AD_Y_CH, AD_Y_NO);
    //GPIO_PFR(TOUCH_PIN) &= ~GPIO_MASK(TOUCH_PIN);
    //GPIO_PU_ON(TOUCH_PIN);
    g_KS_MB9BF568_TouchPanel_Driver.m_global_inX = 0;
    g_KS_MB9BF568_TouchPanel_Driver.m_global_inY = 0;
    g_KS_MB9BF568_TouchPanel_Driver.m_global_touch = 0;
    g_KS_MB9BF568_TouchPanel_Driver.m_samplingTimespan = FM4_TOUCHPANEL_POLL_TIMESPAN;
    g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.Initialize();
    g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.InitializeForISR(KS_MB9BF568_TouchPanel_Driver::TouchCompletion, NULL);
    g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.EnqueueDelta(g_KS_MB9BF568_TouchPanel_Driver.m_samplingTimespan);
    if (!::CPU_GPIO_EnableInputPin(
            TOUCH_PIN,
            FALSE,
            touchIsrProc,
            GPIO_INT_EDGE_BOTH,
            RESISTOR_PULLUP))
    {
        return FALSE;
    }
    return TRUE;
}

BOOL KS_MB9BF568_TouchPanel_Driver::Uninitialize()
{
    if (g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.IsLinked())
    {
        g_KS_MB9BF568_TouchPanel_Driver.m_touchCompletion.Abort();
    }
    if (!::CPU_GPIO_EnableInputPin(
            TOUCH_PIN,
            FALSE,
            NULL,
            GPIO_INT_NONE,
            RESISTOR_PULLUP))
    {
        return FALSE;
    }
    return TRUE;
}

void KS_MB9BF568_TouchPanel_Driver::Poll()
{
    static UINT32 t_state;

    switch(t_state) {
    case 0:
        SENSE_ON;
        t_state++;
        break;
    case 1:
        if (TOUCH_CHK) {
#if 1
            GLOBAL_LOCK(irq);
            SENSE_Y_ON;
            AD_Y_ENABLE;
            volatile int i = 3000;
            while (i-- > 0);
            Touch_AD_Start(AD_Y_CH, AD_Y_NO);
            g_KS_MB9BF568_TouchPanel_Driver.m_global_inY = Touch_AD_Sense(AD_Y_CH, AD_Y_NO);
            AD_Y_DISABLE;
            SENSE_X_ON;
            AD_X_ENABLE;
            i = 3000;
            while (i-- > 0);
            Touch_AD_Start(AD_X_CH, AD_X_NO);
            g_KS_MB9BF568_TouchPanel_Driver.m_global_inX = Touch_AD_Sense(AD_X_CH, AD_X_NO);
            AD_X_DISABLE;
            SENSE_ON;
#endif
            t_state++;
        } else {
            IDLE;
            g_KS_MB9BF568_TouchPanel_Driver.m_global_touch = 0;
            t_state = 0;
        }
        break;
    case 2:
#if 1
        if (TOUCH_CHK) {
            g_KS_MB9BF568_TouchPanel_Driver.m_global_touch = 1;
#ifdef DEBUG_TOUCHPANEL
            lcd_printf("x=%d y=%d\r\n",
                    g_KS_MB9BF568_TouchPanel_Driver.m_global_inX,
                    g_KS_MB9BF568_TouchPanel_Driver.m_global_inY);
#endif
        } else {
            IDLE;
            g_KS_MB9BF568_TouchPanel_Driver.m_global_touch = 0;
            //g_KS_MB9BF568_TouchPanel_Driver.m_global_inX = 0;
            //g_KS_MB9BF568_TouchPanel_Driver.m_global_inY = 0;
        }
#endif
        t_state = 0;
        break;
    default:
        t_state = 0;
        break;
    }
}
